package com.coc.myquartz.quartz;

import java.text.ParseException;
import java.util.Date;
import java.util.List;
import java.util.Map;

import javax.annotation.Resource;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.quartz.CronTrigger;
import org.quartz.Job;
import org.quartz.JobDetail;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.quartz.ObjectAlreadyExistsException;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.SchedulerFactory;
import org.quartz.SimpleTrigger;
import org.quartz.impl.StdSchedulerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;

import com.coc.myquartz.entity.AutoJob;
import com.coc.myquartz.mapper.AutoJobRespository;


/**
 * 定时任务的启动类：
 * 配置格式： Quarz格式: [秒] [分] [小时] [日] [月] [周] [年]
 */
@Component
public class QuartzMain implements InitializingBean , ApplicationContextAware {
    private static final Log logger = LogFactory.getLog(QuartzMain.class);

    private ApplicationContext applicationContext ;
    private SchedulerFactory gSchedulerFactory = new StdSchedulerFactory();

    @Resource
    private AutoJobRespository autoJobRespository;

    public void start() throws SchedulerException, InterruptedException {

        List<AutoJob> list = null;
        try{
            list = autoJobRespository.findAll();
        }catch(Exception ex){
         //   logger.error("查询定时任务失败", ex);
            ex.printStackTrace();
        }

        if (list == null){
            return;
        }

        for (int i = 0; i < list.size(); i++) {
            if (list.get(i).getJob_enable() == 1) { // 启用定时器
                AutoJob job = list.get(i);

                //如果是有时间调度的任务（可以指定年月日时分秒的任务）
                if (list.get(i).getJob_type() == AutoJob.JOB_SCHEDULE) {
                    addJob(job.getJob_name(), job.getJob_name(),
                            job.getJob_name(), job.getJob_name(), job
                                    .getClass_name(), job.getJob_time());
                } else {
                    addSimJob(job.getJob_name(), job
                            .getJob_name(), job.getJob_name(), job
                            .getJob_name(), job.getClass_name(), job
                            .getRepeatcount(), job.getJob_interval(), job
                            .getDelaytime());
                }
            }else { // 不启用的定时器
                AutoJob job = list.get(i);
                /***
                 * 集群控制  对于不启用的定时器，重集群控制表中删除
                 */
                removeJob(job.getJob_name(),job.getJob_name(),job.getJob_name(),job.getJob_name());
            }
        }
        startJobs();
    }

    @Override
    public void afterPropertiesSet() throws Exception {
    	start();
    }


    /**
     * 添加一个定时任务
     *
     * @param jobName
     *            任务名
     * @param jobGroupName
     *            任务组名
     * @param triggerName
     *            触发器名
     * @param triggerGroupName
     *            触发器组名
     * @param jobClass
     *            任务
     * @param time
     *            时间设置，参考quartz说明文档
     * @throws SchedulerException
     * @throws ParseException
     */
    public void addJob(String jobName, String jobGroupName,
                       String triggerName, String triggerGroupName, String jobClass,
                       String time) {
        try {
            Scheduler sched = gSchedulerFactory.getScheduler();
            // 触发器
            CronTrigger trigger = new CronTrigger(triggerName, triggerGroupName);// 触发器名,触发器组
            
//            CronTrigger trigger = new CronTrigger(jobName,jobGroupName,triggerName, triggerGroupName);
            
            trigger.setCronExpression(time);// 触发器时间设定
            trigger.setMisfireInstruction(CronTrigger.MISFIRE_INSTRUCTION_DO_NOTHING);
            
            Class<?> clz = Class.forName(jobClass);

            JobDetail jobDetail = null;
            if (SpringContextJob.class.isAssignableFrom(clz)){

                jobDetail = new JobDetail(jobName, jobGroupName, SpringContextObjectJobDelegate.class);// 任务名，任务组，任务执行类
                jobDetail.setVolatility(true);
                jobDetail.setDurability(true);
                jobDetail.getJobDataMap().put("beanFactory", applicationContext);
                jobDetail.getJobDataMap().put("targetJobClass", Class.forName(jobClass));

            }else{
            	 jobDetail = new JobDetail(jobName, jobGroupName, clz);
                 jobDetail.setJobClass(clz);
            }
           
            sched.scheduleJob(jobDetail, trigger);
        }catch (ObjectAlreadyExistsException e) {
            logger.error("集群啓動會抛出磁異常，屬於正常現象！！！！，攔截不做任何處理，否則不能正常啓動！！！");
		} catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException(e);   // 垃圾 
        }
    }

    /**
     * 添加一个间隔任务
     * @param jobName
     * @param jobGroupName
     * @param triggerName
     * @param triggerGroupName
     * @param jobClass
     * @param repeatCount
     * @param repeatInterval
     * @param delayTime
     *
     */
    public void addSimJob(String jobName,String jobGroupName,String triggerName, String triggerGroupName,String jobClass,int repeatCount,String repeatInterval,String delayTime){
        try {
            Scheduler sched = gSchedulerFactory.getScheduler();
            long startTime = System.currentTimeMillis()+Long.parseLong(delayTime);
            SimpleTrigger trigger = new SimpleTrigger(triggerName, triggerGroupName,new Date(
                    startTime) , null, repeatCount, Long.parseLong(repeatInterval));

            trigger.setMisfireInstruction(SimpleTrigger.MISFIRE_INSTRUCTION_RESCHEDULE_NEXT_WITH_REMAINING_COUNT);

            Class<?> clz = Class.forName(jobClass);
            JobDetail jobDetail = null;
            if (SpringContextJob.class.isAssignableFrom(clz)){
            	
                jobDetail = new JobDetail(jobName, jobGroupName, SpringContextObjectJobDelegate.class);// 任务名，任务组，任务执行类
                jobDetail.setVolatility(true);
                jobDetail.setDurability(true);
                jobDetail.getJobDataMap().put("beanFactory", applicationContext);
                jobDetail.getJobDataMap().put("targetJobClass", Class.forName(jobClass));

            }else{
                jobDetail = new JobDetail(jobName, jobGroupName, clz);
                jobDetail.setJobClass(clz);
            }

            //加入到调度器中
            sched.scheduleJob(jobDetail, trigger);
        }catch (ObjectAlreadyExistsException e) {
            logger.error("集群啓動會抛出磁異常，屬於正常現象！！！！，攔截不做任何處理，否則不能正常啓動！！！");
		} catch (SchedulerException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (Exception e){
            throw new RuntimeException(e);
        }

    }


    /**
     * 修改一个任务的触发时间
     *
     * @param triggerName
     * @param triggerGroupName
     * @param time
     */
    public void modifyJobTime(String triggerName,
                              String triggerGroupName, String time) {
        try {
            Scheduler sched = gSchedulerFactory.getScheduler();
            CronTrigger trigger = (CronTrigger) sched.getTrigger(triggerName,
                    triggerGroupName);
            if (trigger == null) {
                return;
            }
            String oldTime = trigger.getCronExpression();
            if (!oldTime.equalsIgnoreCase(time)) {
                CronTrigger ct = (CronTrigger) trigger;
                // 修改时间
                ct.setCronExpression(time);
                // 重启触发器
                sched.resumeTrigger(triggerName, triggerGroupName);
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 移除一个任务
     *
     * @param jobName
     * @param jobGroupName
     * @param triggerName
     * @param triggerGroupName
     */
    public void removeJob(String jobName, String jobGroupName,
                          String triggerName, String triggerGroupName) {
        try {
            Scheduler sched = gSchedulerFactory.getScheduler();
            sched.pauseTrigger(triggerName, triggerGroupName);// 停止触发器
            sched.unscheduleJob(triggerName, triggerGroupName);// 移除触发器
            sched.deleteJob(jobName, jobGroupName);// 删除任务
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        }
    }

    /**
     * 启动所有定时任务
     */
    public void startJobs() {
        try {
            Scheduler sched = gSchedulerFactory.getScheduler();
            sched.start();
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        }
    }

    /**
     * 关闭所有定时任务
     */
    public void shutdownJobs() {
        try {
            Scheduler sched = gSchedulerFactory.getScheduler();
            if (!sched.isShutdown()) {
                sched.shutdown();
            }
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        }
    }


    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }


    public static class SpringContextObjectJobDelegate implements Job {

        private BeanFactory beanFactory;
        private Class<? extends Job> targetJobClass;
        private Job targetJobObject;

        public SpringContextObjectJobDelegate(){}

        public void setBeanFactory(BeanFactory beanFactory){
            this.beanFactory = beanFactory;
        }

        public void setTargetJobClass(Class<? extends Job> jobClass){
            this.targetJobClass = jobClass;
        }

        @SuppressWarnings({ "unchecked", "rawtypes" })
        @Override
        public void execute(JobExecutionContext arg0) throws JobExecutionException {

            Map map = arg0.getMergedJobDataMap();
            targetJobClass = (Class<? extends Job>) map.get("targetJobClass");
            beanFactory = (BeanFactory) map.get("beanFactory");

            getTargetJobObject().execute(arg0);
        }

        private Job getTargetJobObject() {

            if (targetJobObject == null){
                targetJobObject = beanFactory.getBean(getTargetJobClass());
                if(targetJobObject == null){
                    throw new RuntimeException("Spring 容器中无法找到"+getTargetJobClass()+"的对象实例");
                }
            }

            return targetJobObject;
        }

        private Class<? extends Job> getTargetJobClass() {
            return targetJobClass;
        }

    }


}
